package com.example.onlinejudge.service;

import com.example.onlinejudge.component.FileUtil;
import com.example.onlinejudge.controller.CommandUtilController;
import com.example.onlinejudge.model.Answer;
import com.example.onlinejudge.model.Question;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Service
public class TaskService {
    @Autowired
    private CommandUtilController commandUtilController;
    @Autowired
    private FileUtil fileUtil;
    //表示临时文件所在目录
    private String WORK_DIR = null;
    //代码的类名
    private String CLASS = null;
    //要编译的代码文件名.
    private String CODE = null;
    //存放编译错误信息的文件名
    private String COMPILE_ERROR = null;
    //存放运行时的标准输出的文件名
    private String STDOUT = null;
    //存放运行时的标准错误的文件名
    private String STDERR = null;

    public TaskService(){
        //使用UUID生成唯一路径
        WORK_DIR = "./tmp/" + UUID.randomUUID()+ "/";
        CLASS = "Solution";
        CODE = WORK_DIR + "Solution.java";
        COMPILE_ERROR = WORK_DIR + "compileError.txt";
        STDOUT = WORK_DIR + "stdout.txt";
        STDERR = WORK_DIR + "stderr.txt";
    }

    // 这个 compile.Task 类提供的核心方法, 就叫做 compileAndRun, 编译+运行 的意思.
    // 参数: 要编译运行的 java 源代码.
    // 返回值: 表示编译运行的结果. 编译出错/运行出错/运行正确.....
    public Answer compileAndRun(Question question) throws IOException, InterruptedException {
        Answer answer=new Answer();
        //准备好临时目录
        File workDir=new File(WORK_DIR);
        if (!workDir.exists()) {
            // 创建多级目录.
            workDir.mkdirs();
        }
        // 1.把question中的code写入到Solution.java
        fileUtil.writeFile(CODE,question.getCode());
        // 2. 创建子进程, 调用 javac 进行编译
        //如果编译出错则将错误信息写入stderr用专门的文件保存

        //构建编译指令
        String compileCmd = String.format("javac -encoding utf8 %s -d %s", CODE, WORK_DIR);
        commandUtilController.run(compileCmd, null, COMPILE_ERROR);
        // 如果编译出错了, 错误信息就被记录到 COMPILE_ERROR 这个文件. 如果没有编译出错, 这个文件是空文件.
        String compileError=fileUtil.readFile(COMPILE_ERROR);
        if (!compileError.equals("")) {
            // 编译错误直接返回 compile.Answer, 让 compile.Answer 里面记录编译的错误信息.
            System.out.println("编译出错!");
            answer.setError(1);
            answer.setReason(compileError);
            return answer;
        }
        //3.创建子进程, 调用 java 命令并执行
        // 同时获取到标准错误和标准输出
        String runCmd = String.format("java -classpath %s %s", WORK_DIR, CLASS);
        commandUtilController.run(runCmd,STDOUT,STDERR);
        String runError=fileUtil.readFile(STDERR);
        if(!runError.equals("")){
            System.out.println("运行错误");
            answer.setError(2);
            answer.setReason(runError);
            return answer;
        }
        answer.setError(0);
        answer.setStdout(fileUtil.readFile(STDOUT));
        return answer;
    }

    //检测恶意代码
    public boolean checkCodeSafe(String code){
        List<String> blackList=new ArrayList<>();
        blackList.add("Runtime");
        blackList.add("exec");
        blackList.add("java.io");
        blackList.add("java.net");

        for(String target:blackList){
            int idx=code.indexOf(target);
            if(idx>=0){
                return false;
            }
        }
        return true;
    }
}
